{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "# Iterative Phase Estimation via the Cloud\n",
        "\n",
        "This sample code and notebook was written by members of KPMG Quantum team in Australia. It aims to demonstrate expanded capabilities of Basic Measurement Feedback targets and makes use of bounded loops, classical function calls at run time, nested conditional if statements, mid circuit measurements and qubit reuse. \n",
        "\n",
        "\n",
        "### Two Dimensional Inner Product Calculation Using Iterative Phase Estimation on Three Qubits\n",
        "\n",
        "This notebook demonstrates an iterative phase estimation within Q#. It will use iterative phase estimation to calculate an inner product between two 2-dimensional vectors encoded on a target qubit and an ancilla qubit. An additional control qubit is also initialised which will be the only qubit used for measurement.\n",
        "\n",
        "The circuit begins by encoding the pair of vectors on the target qubit and the ancilla qubit. It then applies an Oracle operator to the entire register, controlled off the control qubit (which is set up in the $\\ket +$ state). The controlled Oracle operator generates a phase on the $\\ket 1$ state of the control qubit. This can then be read by applying a H gate to the control qubit to make the phase observable when measuring.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.connect \"\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "##### Open Name Spaces Required\n",
        "There are a number of Name Spaces required for this set of code. They are opened in the block below."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "open Microsoft.Quantum.Intrinsic;\n",
        "open Microsoft.Quantum.Math;\n",
        "open Microsoft.Quantum.Arrays;\n",
        "open Microsoft.Quantum.Measurement;\n",
        "open Microsoft.Quantum.Convert;"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Encoding vectors\n",
        "\n",
        "The vectors v and c are to be encoded onto the target qubit and the ancilla qubit. The vector $v = (cos(\\frac{\\theta_1}{2}),sin(\\frac{\\theta_1}{2}))$ can be represented by the quantum state $\\ket v = cos(\\frac{\\theta_1}{2})\\ket 0 + sin(\\frac{\\theta_1}{2})\\ket 1$, similarly $c$ can be constructed using $\\theta_2$. \n",
        "\n",
        "A Y rotation applied to a target qubit in the $\\ket 0$ state:\n",
        "\n",
        "$$RY(\\theta)\\ket 0 = e^{iY\\theta/2}\\ket 0 = cos(\\frac{\\theta}{2})\\ket 0 + sin(\\frac{\\theta}{2})\\ket 1$$\n",
        "\n",
        "**Note**: <u> A factor of 2 </u> is present here on theta. An application of a $RY(2\\pi)$ gate on $\\ket 0$ gives the state $-\\ket 0$ and would encode the vector $(-1,0)$. This phase cannot be considered a global phase and removed as the entire register will be entangled.\n",
        "\n",
        "The register of the target qubit and ancilla qubit is,\n",
        "\n",
        "$$\\ket  \\Psi = \\ket {\\Psi_\\text{Target qubit}}\\ket {\\Psi_\\text{Ancilla qubit}}$$\n",
        "The state to be created is on the target qubit and the ancilla qubit is,\n",
        "\n",
        "$$\\ket{\\Psi}=\\frac{1}{\\sqrt{2}}(\\ket{v}\\ket{+}+\\ket{c}\\ket{-}),$$\n",
        "\n",
        "\n",
        "which also takes the form,\n",
        "\n",
        "$$\\ket{\\Psi} = \\frac{1}{2}(\\ket{v}+\\ket{c})\\ket{0}+\\frac{1}{2}(\\ket{v}-\\ket{c})\\ket{1}.$$\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "operation StateInitialisation(TargetReg : Qubit, AncilReg : Qubit, theta_1 : Double, theta_2 : Double) : Unit is Adj + Ctl { //This is state preperation operator A for encoding the 2D vector (page 7)\n",
        "    H(AncilReg);\n",
        "    // Arbitrary controlled rotation based on theta. This is vector v.\n",
        "    Controlled R([AncilReg], (PauliY, theta_1, TargetReg));        \n",
        "    // X gate on ancilla to change from |+> to |->.                                                    \n",
        "    X(AncilReg);\n",
        "    // Arbitrary controlled rotation based on theta. This is vector c.                                                   \n",
        "    Controlled R([AncilReg], (PauliY, theta_2, TargetReg));        \n",
        "    X(AncilReg);                                                  \n",
        "    H(AncilReg);                                                  \n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## The Oracle\n",
        "\n",
        "An oracle G needs to be constructed such that it generates an eigenphase on the state encoded on the target qubit and the ancilla qubit. The construction of this oracle is unimportant to the demonstration within this notebook, but the operation it applies is,\n",
        "\n",
        "$$G\\ket \\Psi = e^{2\\pi i\\phi} \\ket \\Psi.$$\n",
        "\n",
        "where the inner product $\\braket {v|c}$ is contained within the phase $\\phi$, which is bound between [0,1]. When applied controlled on the control qubit which begins in that state $\\ket{\\Psi_\\text{Control Qubit}} = \\ket +$,\n",
        "\n",
        "$$\\begin{aligned}\n",
        "    \\text{Controlled }G \\ket{\\Psi_\\text{Control Qubit}} \\ket \\Psi  & = \\frac {1}{\\sqrt{2}} (\\ket 0 \\ket \\Psi + e^{2\\pi i\\phi}\\ket 1 \\ket \\Psi )\\\\\n",
        "    & =\\frac {1}{\\sqrt{2}} (\\ket 0 + e^{2\\pi i\\phi}\\ket 1) \\ket \\Psi\n",
        "\\end{aligned}$$\n",
        "\n",
        "Now the control qubit contains the phase $\\phi$ which relates to the inner product $\\braket {v|c}$\n",
        "\n",
        "$$\\ket{\\Psi_\\text{Control Qubit}} = \\frac {1}{\\sqrt{2}} (\\ket 0 + e^{2\\pi i\\phi}\\ket 1)$$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "operation GOracle(TargetReg : Qubit, AncilReg : Qubit, theta_1 : Double, theta_2 : Double) : Unit is Adj + Ctl {\n",
        "    Z(AncilReg);\n",
        "    within {\n",
        "        Adjoint StateInitialisation(TargetReg, AncilReg, theta_1, theta_2);\n",
        "        X(AncilReg);                                                        \n",
        "        X(TargetReg);\n",
        "    }   \n",
        "    apply {\n",
        "        Controlled Z([AncilReg],TargetReg);\n",
        "    }\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Iteration\n",
        "\n",
        "Now for the iterative part of the circuit. For n measurements, consider that the phase can be represented as a binary value $\\phi$, and that applying $2^n$ oracles makes the nth binary point of the phase observable (through simple binary multiplication, and modulus $2\\pi$). The value of the control qubit can be readout, placed in a classical register and the qubit reset for use in the next iteration. The next iteration applies $2^{n-1}$ oracles, correcting phase on the control qubit dependent on the nth measurement. The state on the control qubit can be represented as,\n",
        "\n",
        "$$ \\ket {\\Psi_{\\text{Control Qubit}}} = \\ket 0 + e^{2\\pi i\\phi}\\ket 1 $$\n",
        "\n",
        "where $\\phi = 0.\\phi_0\\phi_1\\phi_2\\phi_3$...\n",
        "\n",
        "Applying $2^n$ controlled oracles gives the state on the control qubit,\n",
        "\n",
        "$$ G^{2^n}\\ket {\\Psi_{\\text{Control Qubit}}} = \\ket 0 + e^{2\\pi i 0.\\phi_n\\phi_{n+1}\\phi_{n+2}\\phi_{n+3}...}\\ket 1 $$\n",
        "\n",
        "Consider that the phase has no terms deeper than $\\phi_n$ (ie, terms $\\phi_{n+1},\\phi_{n+2}, \\text{etc}$),\n",
        "\n",
        "$$ G^{2^n}\\ket {\\Psi_{\\text{Control Qubit}}} = \\ket 0 + e^{2\\pi i 0.\\phi_n}\\ket 1 $$\n",
        "\n",
        "Now the value $\\phi_n$ can be observed with a H gate and a measurement projecting along the Z axis. Resetting the control qubit and applying the oracle $2^{n-1}$ times,\n",
        "\n",
        "$$ G^{2^{n-1}}\\ket {\\Psi_{\\text{Control Qubit}}} = \\ket 0 + e^{2\\pi i 0.\\phi_{n-1}\\phi_n}\\ket 1 $$\n",
        "\n",
        "Using the previous measured value for $\\phi_n$, the additional binary point can be rotated out.\n",
        "\n",
        "$$ RZ(-2\\pi \\times 0.0\\phi_n)G^{n-1}\\ket {\\Psi_{\\text{Control Qubit}}} = \\ket 0 + e^{2\\pi i 0.\\phi_{n-1}}\\ket 1 $$\n",
        "\n",
        "This process is iteratively applied for some bit precision n to obtain the phase $0.\\phi_0\\phi_1\\phi_2...\\phi_{n}$. The value is stored as a binary value $x = \\phi_0\\phi_1\\phi_2...\\phi_{n}$ as only integers are manipulatable at runtime currently.\n",
        "\n",
        "As the readout tells nothing of either vector, only the inner product between them, the states on the target qubit and ancilla qubit <u>remain in the same state</u> throughout the process!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "operation IterativePhaseEstimation(TargetReg : Qubit, AncilReg : Qubit, theta_1 : Double, theta_2 : Double, Measurements : Int) : Int{\n",
        "    use ControlReg = Qubit();\n",
        "    mutable MeasureControlReg = [Zero, size = Measurements];\n",
        "    mutable bitValue = 0;\n",
        "    //Apply to initialise state, this is defined by the angles theta_1 and theta_2\n",
        "    StateInitialisation(TargetReg, AncilReg, theta_1, theta_2);                                     \n",
        "    for index in 0 .. Measurements - 1{                                                             \n",
        "        H(ControlReg);\n",
        "        //Don't apply rotation on first set of oracles                                                                             \n",
        "        if index > 0 {\n",
        "            //Loop through previous results                                                                              \n",
        "            for index2 in 0 .. index - 1{                                                           \n",
        "                if MeasureControlReg[Measurements - 1 - index2] == One{\n",
        "                    //Rotate control qubit dependent on previous measurements and number of measurements\n",
        "                    let angle = -IntAsDouble(2^(index2))*PI()/(2.0^IntAsDouble(index));                   \n",
        "                    R(PauliZ, angle, ControlReg);                                                   \n",
        "                }\n",
        "            }\n",
        "            \n",
        "        }\n",
        "        let powerIndex = (1 <<< (Measurements - 1 - index));\n",
        "        //Apply a number of oracles equal to 2^index, where index is the number or measurements left\n",
        "        for _ in 1 .. powerIndex{                                                                   \n",
        "                Controlled GOracle([ControlReg],(TargetReg, AncilReg, theta_1, theta_2));\n",
        "            }\n",
        "        H(ControlReg);\n",
        "        //Make a measurement mid circuit \n",
        "        set MeasureControlReg w/= (Measurements - 1 - index) <- MResetZ(ControlReg);                \n",
        "        if MeasureControlReg[Measurements - 1 - index] == One{\n",
        "            //Assign bitValue based on previous measurement\n",
        "            set bitValue += 2^(index);                                                              \n",
        "                                                                                                    \n",
        "        }\n",
        "    }\n",
        "    return bitValue;\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "Finally to calculate the inner product from the measured value,\n",
        "\n",
        "$$\\braket {v|c} = -cos(2\\pi x / 2^n)$$\n",
        "\n",
        "where $x = \\phi_0\\phi_1\\phi_2...\\phi_{n}$. The denominator within the cosine function is to shift the binary point to match the original value $\\phi$.\n",
        "\n",
        "**Note**: For inner product that are not -1 or 1, the solutions are paired with a value difference of $2^{n-1}$. For example for n=3 measurements, the measured bit value of 2 would also have a pair solution of 6. Either of these values produce the same value of the inner product when input as the variable to the even function cosine (resulting in an inner product of 0 in this example).\n",
        "\n",
        "**Note**: For inner product solutions between the discrete bit precision, a distribution of results will be produced based on where the inner product lies between the discrete bit value. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "function CalculateInnerProduct(Results : Int, theta_1 : Double, theta_2 : Double, Measurements : Int): Unit{\n",
        "    //Convert to the final inner product\n",
        "    let DoubleVal = PI() * IntAsDouble(Results) / IntAsDouble(2 ^ (Measurements-1));\n",
        "    let InnerProductValue = -Cos(DoubleVal);                                                      \n",
        "    Message(\"The Bit Value measured is:\");\n",
        "    Message($\"{Results}\");\n",
        "    Message(\"The Inner Product is:\");\n",
        "    Message($\"{InnerProductValue}\");\n",
        "    Message(\"The True Inner Product is:\");\n",
        "    Message($\"{Cos(theta_1/2.0)*Cos(theta_2/2.0)+Sin(theta_1/2.0)*Sin(theta_2/2.0)}\");\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "operation InnerProduct() : (Int, Double, Double, Int){\n",
        "    //Specify the angles for inner product\n",
        "    let theta_1 = 0.0;                                                                           \n",
        "    let theta_2 = 0.0;\n",
        "    let Measurements = 3;\n",
        "    //Create target register\n",
        "    use TargetReg = Qubit();\n",
        "    //Create ancilla register                                                                   \n",
        "    use AncilReg = Qubit();\n",
        "    //Run iterative phase estimation                                                                   \n",
        "    let Results = IterativePhaseEstimation(TargetReg, AncilReg, theta_1, theta_2, Measurements);\n",
        "    Reset(TargetReg);                                           \n",
        "    Reset(AncilReg);\n",
        "    return (Results, theta_1, theta_2, Measurements);\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "//Operation for calculating the inner product on local simulators\n",
        "//This operation will output additional classical calculations\n",
        "operation SimulateInnerProduct() : Unit{                                                        \n",
        "        let (Results, theta_1, theta_2, Measurements) = InnerProduct();                             \n",
        "        CalculateInnerProduct(Results, theta_1, theta_2, Measurements);                             \n",
        "    }"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%simulate SimulateInnerProduct"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "The inner product operation is reconstructed to fit within the requirements of the target. In this case, calls like \"Message\" and manipulation of doubles is not allowed. Therefore the output will be the bit value as generated by the IterativePhaseEstimation operation."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "//Operation for calculating the inner product on hardware or emulators\n",
        "operation HardwareInnerProduct() : Int{                                                         \n",
        "    let (Results,_,_,_) = InnerProduct();\n",
        "    return Results;                                                                             \n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "Specify the target.\n",
        "\n",
        "**Note**: The target requires a target execution profile that supports basic measurement feedback."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.target quantinuum.sim.h1-1e"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.target-capability AdaptiveExecution"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "Submit the job to the target. The circuit is approximately 0.4 EHQC each shot. The number of shots specified is 128, but this can be increased to reduce the variance of the result, up to some stable distribution. The total EHQCs for a job can be viewed within the Azure portal under \"Job management\". Selecting the desired job, the cost estimation can be viewed.\n",
        "\n",
        "**Note**: Choosing input parameters which only has one solution state (inner produces of -1 or 1) are ideal for visibility at a low number of shots."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.submit HardwareInnerProduct shots = 128\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "Check the status of the job specified."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.status"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "When the job is complete, output a histogram of results.\n",
        "\n",
        "The results should show a majority in the solution states."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.output"
      ]
    }
  ],
  "metadata": {
    "kernel_info": {
      "name": "iqsharp"
    },
    "kernelspec": {
      "display_name": "Q#",
      "language": "qsharp",
      "name": "iqsharp"
    },
    "language_info": {
      "file_extension": ".qs",
      "mimetype": "text/x-qsharp",
      "name": "qsharp",
      "version": "0.27"
    },
    "nteract": {
      "version": "nteract-front-end@1.0.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
